#pragma once
#include<iostream>
#include"RangeEle.h"
#include"Block.h"
#include"BlockDel.h"
#include"SkipListIndex.h"
#include"PageExchange.h"
#include"AsyncIncreWriteThread.h"
using namespace std;

//资源池 多例模式几种资源几种池。
//依据hash值，使用分区间容器。分区间加锁增加并发 而且可以动态添加分区容器
class ResourcePool{
public:
    static ResourcePool* blockPool;
    static ResourcePool* blockDelPool;
    static ResourcePool* indexPool;
    static ResourcePool* bitMapPool;

    CACHETYPE cacheType;

    int singleCacheSize;

    hash<string> hashUtil;

    RangeEle<PageCache*>* containerPartition;

    QReadWriteLock structLock; //控制对于整体节点结构的访问或者修改。

    vector<PageCache*> createContainers(const int& cacheCount){
        vector<PageCache*> containers;
        for(int i = 0; i < cacheCount; i++){
            containers.push_back(CacheUtil::getPageCache(cacheType, singleCacheSize));
        }
        return containers;
    }

    ResourcePool(const CACHETYPE& cacheType, const int& singleCacheSize, const int& cacheCount){
        this->cacheType = cacheType;
        this->singleCacheSize = singleCacheSize;
        this->containerPartition = new RangeEle<PageCache*>(createContainers(cacheCount), cacheCount);
    }

    //获取值对应的容器
    PageCache* getProbCache(const string& str){
        return containerPartition->getProbEle(hashUtil(str));
    }

    //扩容资源池
    void addCache(const uint64_t& leftRange){
        structLock.lockForWrite(); // 整体结构修改 上写锁
        PageCache* cache = containerPartition->getProbEle(leftRange);
        cache->clear();
        containerPartition->addEle(leftRange, CacheUtil::getPageCache(cacheType, singleCacheSize));
        structLock.unlock();
    }

    //将资源从磁盘中加载到内存
    ResourceManage* loadAndGetResource(PageCache* cache, const ResourceInfo& info){
        ResourceManage* res = createResource(info);
        //判断异步写入线程是否没将要读取的资源修改完成 如果没完成就强制写入（同步）
        if(AsyncIncreWriteThread::isThreadUnIncreWriteToDisk(info)){
            AsyncIncreWriteThread::threadForceIncreWriteToDisk();
        }
        res->readFromDisk();
        cache->put(info.getRelativePath(), res);
        return res;
    }

    //从资源池获取资源
    ResourceManage* getResource(const ResourceInfo& info){
        ResourceManage* res;
        string identifer = info.getRelativePath();
        structLock.lockForRead();   //防止在操作时修改结构
        PageCache* cache = getProbCache(identifer);
        structLock.unlock();
        cache->lock();
        try {
            res = cache->get(identifer);
        }  catch (string) {//如果没有获取到，从磁盘加载并重新获取。
            res = loadAndGetResource(cache, info);
        }
        cache->unLock();
        res->getHoldLock(READ);     //标记持有资源
        return res;
    }

    bool putResource(const ResourceInfo& info, ResourceManage* res){
        string identifer = info.getRelativePath();
        structLock.lockForRead(); //这里注意 他对单个节点来说是修改操作， 但是对于整体结构没有改变。所以上读锁。
        PageCache* cache = getProbCache(identifer);
        structLock.unlock();
        bool isSuccess = true;
        cache->lock();
        try {
            cache->put(identifer, res);
        }  catch (string) { //如果已在资源池会抛异常。设为失败。
            isSuccess = false;
        }
        cache->unLock();
        return isSuccess;
    }

    static ResourceManage* createResource(const ResourceInfo& info){
        ResourceManage* res;
        DISKTYPE diskType = info.getDiskType();
        if(diskType == BLOCK)
            res = new Block(info);
        else if(diskType == BLOCKDEL)
            res = new BlockDel(info);
        else if(diskType == SLINDEX)
            res = new SkipListIndex(info);
        else if(diskType == BITMAP)
            res = new BitMap(info);
        else
            throw string("type error");
        return res;
    }

public:

    ~ResourcePool(){
        MemUtil::clearPtrMem(containerPartition);
    }

    static void initBlockPool(const CACHETYPE& cacheType, const int& singleCacheSize, const int& cacheCount){
        blockPool = new ResourcePool(cacheType, singleCacheSize, cacheCount);
    }

    static void initBlockDelPool(const CACHETYPE& cacheType, const int& singleCacheSize, const int& cacheCount){
        blockDelPool = new ResourcePool(cacheType, singleCacheSize, cacheCount);
    }

    static void initIndexPool(const CACHETYPE& cacheType, const int& singleCacheSize, const int& cacheCount){
        indexPool = new ResourcePool(cacheType, singleCacheSize, cacheCount);
    }

    static void initBitMapPool(const CACHETYPE& cacheType, const int& singleCacheSize, const int& cacheCount){
        bitMapPool = new ResourcePool(cacheType, singleCacheSize, cacheCount);
    }


    static ResourceManage* getRes(const ResourceInfo& info){
        DISKTYPE diskType = info.getDiskType();
        if(diskType == BLOCK)
            return blockPool->getResource(info);
        else if(diskType == BLOCKDEL)
            return blockDelPool->getResource(info);
        else if(diskType == SLINDEX)
            return indexPool->getResource(info);
        else if(diskType == BITMAP)
            return bitMapPool->getResource(info);
        throw string("type error");
    }

    static bool putRes(const ResourceInfo& info, ResourceManage* res){
        DISKTYPE diskType = info.getDiskType();
        if(diskType == BLOCK)
            return blockPool->putResource(info, res);
        else if(diskType == BLOCKDEL)
            return blockDelPool->putResource(info, res);
        else if(diskType == SLINDEX)
            return indexPool->putResource(info, res);
        else if(diskType == BITMAP)
            return bitMapPool->putResource(info, res);
        throw string("type error");
    }

    static ResourcePool* getBlockPool(){
        return blockPool;
    }


    static ResourcePool* getBlockDelPool(){
        return blockDelPool;
    }


    static ResourcePool* getIndexPool(){
        return indexPool;
    }

};



